# /**
# * Copyright (c) 2022 Beijing Jiaotong University
# * PhotLab is licensed under [Open Source License].
# * You can use this software according to the terms and conditions of the [Open Source License].
# *
# * You may obtain a copy of [Open Source License] at: [https://open.source.license/]
# *
# * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# *
# * See the [Open Source License] for more details.
# */
# /**
# * Author: Shihao Xie
# * Created: Apr. 25, 2023
# * Supported by: National Key Research and Development Program of China
# */

from phot_server.core.exception import ParseError
from phot_server.rpc.graph_data_type.ttypes import (
    RenderPort,
    PhotParameter,
    PortDirection,
    PortAspect,
    RenderRect,
)
from phot_server.rpc.library_data_type.ttypes import PhotComponentPrototype
from phot_server.utils.toml_parser import parse_toml, save_toml
from phot_server.utils.type_convert import (
    object_to_dict,
    list_to_PhotParameter_dict,
    dict_to_RenderPort_list,
    dict_to_PhotParameter_dict,
)


def parse_phtc(path: str) -> PhotComponentPrototype:
    component_dict = parse_toml(path)
    if component_dict.get("Component", None) is None:
        raise ParseError("Photlab Component File format error. ", path)

    prototype = dict_to_PhotComponentPrototype(component_dict)
    return prototype


def save_phtc(path: str, prototype: PhotComponentPrototype):
    save_toml(path, PhotComponentPrototype_to_dict(prototype))


def parse_component_toml(file_path: str) -> PhotComponentPrototype:
    component_dict = parse_toml(file_path)
    component_obj = PhotComponentPrototype()
    component_obj.parallelComputingMethod = "CPU"
    component_obj.name = list(component_dict.keys())[0]
    component = component_dict[component_obj.name]
    component_obj.isClass = component["is_class"]
    component_obj.nameTag = component.get("frontend_name", None)
    component_obj.iconName = component.get("icon_name", None)
    component_obj.description = component["description"]
    component_obj.isAnalyzer = component["is_analyzer"]
    component_obj.rect = RenderRect()

    if component.get("methods", None) is not None:
        component_obj.calculationMethod = list(component["methods"].keys())[0]
        component_obj.ports = parse_component_toml_port(
            list(component["methods"].values())[0]
        )
    else:
        component_obj.calculationMethod = component_obj.name
        port_dict = {}
        if component.get("input", None) is not None:
            port_dict["input"] = component["input"]
        if component.get("output", None) is not None:
            port_dict["output"] = component["output"]
        component_obj.ports = parse_component_toml_port(port_dict)
    component_obj.parameters = parse_component_toml_parameter(component)
    return component_obj


# def parse_component_toml_parameter(parameter_dict: dict) -> list:
#     parameters = []
#     for name, parameter in parameter_dict.items():
#         if isinstance(parameter, dict) and name not in ["input", "output"]:
#             if parameter.get("parameters", None) is not None:
#                 for k, v in parameter["parameters"].items():
#                     param = PhotParameter(name=k,
#                                           value=v.get("value", ""),
#                                           dataType=v.get("data_type", "float"),
#                                           unit=v.get("unit", ""),
#                                           photUnit=v.get("photUnit", ""),
#                                           nameTag=v.get("frontend_name", None),
#                                           category=name
#                                           )
#                     parameters.append(param)
#             if name == "parameters":
#                 for k, v in parameter.items():
#                     param = PhotParameter(name=k,
#                                           value=v.get("value", None),
#                                           dataType=v.get("data_type", "float"),
#                                           unit=v.get("unit", ""),
#                                           photUnit=v.get("photUnit", ""),
#                                           nameTag=v.get("frontend_name", None)
#                                           )
#                     parameters.append(param)
#     return parameters


def parse_component_toml_parameter(parameter_dict: dict) -> dict:
    parameters = {"": []}
    for name, parameter in parameter_dict.items():
        if isinstance(parameter, dict) and name not in ["input", "output"]:
            if parameter.get("parameters", None) is not None:
                if name not in parameters.keys():
                    parameters[name] = []
                for k, v in parameter["parameters"].items():
                    param = PhotParameter(
                        name=k,
                        value=v.get("value", ""),
                        dataType=v.get("data_type", "float"),
                        unit=v.get("unit", ""),
                        photUnit=v.get("photUnit", ""),
                        nameTag=v.get("frontend_name", None),
                        category=name,
                    )
                    parameters[name].append(param)
            elif name == "parameters":
                for k, v in parameter.items():
                    param = PhotParameter(
                        name=k,
                        value=v.get("value", None),
                        dataType=v.get("data_type", "float"),
                        unit=v.get("unit", ""),
                        photUnit=v.get("photUnit", ""),
                        nameTag=v.get("frontend_name", None),
                    )
                    parameters[""].append(param)
    return parameters


def parse_component_toml_port(port_dict: dict) -> list:
    ports = []
    port_id = 0
    loc_point = [2, 1, 3, 0, 4]
    for direction in port_dict.keys():
        location = 0
        for phot_name, front_name in port_dict[direction].items():
            port = RenderPort(
                portId=port_id,
                location=loc_point[location],
                direction=(
                    PortDirection.IN if direction == "input" else PortDirection.OUT
                ),
                aspect=PortAspect.LEFT if direction == "input" else PortAspect.RIGHT,
                name=phot_name,
                description=front_name,
            )
            location += 1
            port_id += 1
            ports.append(port)
    return ports


def PhotComponentPrototype_to_dict(prototype: PhotComponentPrototype,is_add_physical=None) -> dict:
    component_dict = object_to_dict(prototype)

    parameters = component_dict["parameters"]
    parameter_category = dict()
    for category, params in parameters.items():
        for parameter in params:
            if category is None or category == "":
                category = parameter.get("category", "physical")
            if parameter_category.get(category, None) is None:
                parameter_category[category] = []
            parameter.pop("category", None)
            parameter_category[category].append(parameter)
    component_dict["parameters"] = dict()
    for category, parameters in parameter_category.items():
        component_dict["parameters"][category] = parameters

    ports = component_dict.pop("ports")
    input_dict = {}
    output_dict = {}
    for port in ports:
        if int(port["direction"]) == PortDirection.IN:
            input_dict[port["name"]] = port["description"]
        elif int(port["direction"]) == PortDirection.OUT:
            output_dict[port["name"]] = port["description"]

    if len(input_dict) != 0:
        component_dict["inputs"] = input_dict
    if len(output_dict) != 0:
        component_dict["outputs"] = output_dict
    component_dict = {"Component": component_dict}
    return component_dict


def dict_to_PhotComponentPrototype(component_dict: dict) -> PhotComponentPrototype:
    try:
        component_dict = component_dict["Component"]
        port_dict = {}
        if component_dict.get("inputs", None) is not None:
            port_dict["inputs"] = component_dict["inputs"]
        if component_dict.get("outputs", None) is not None:
            port_dict["outputs"] = component_dict["outputs"]
        prototype = PhotComponentPrototype(
            name=component_dict.get("name", None),
            calculationMethod=component_dict.get("calculationMethod", None),
            parallelComputingMethod=component_dict.get("parallelComputingMethod", None),
            isAnalyzer=component_dict.get("isAnalyzer", None),
            isClass=component_dict.get("isClass", None),
            description=component_dict.get("description", None),
            nameTag=component_dict.get("nameTag", None),
            category=component_dict.get("category", None),
            path=component_dict.get("path", None),
            iconName=component_dict.get("iconName", None),
            parameters=(
                list_to_PhotParameter_dict(component_dict["parameters"])
                if isinstance(component_dict.get("parameters", None), list)
                else dict_to_PhotParameter_dict(component_dict["parameters"])
            ),
            ports=dict_to_RenderPort_list(port_dict),
            rect=RenderRect(),
        )
        return prototype
    except KeyError as e:
        raise ParseError("Missing attribute in graph ", e)
